<?php

namespace App\Game;

use App\Models\Log\Earning;
use GuzzleHttp\Client;
use App\Models\Conf\Site;
use App\Models\Inf\Player;
use Illuminate\Http\Request;
use App\Models\Log\SiteScore;
use Dingo\Api\Routing\Helpers;
use App\Contract\GameInterface;
use App\Models\Def\MainGamePlat;
use App\Models\Log\PlayerTransfer;
use App\Models\Conf\SiteMainGamePlat;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\TooManyRedirectsException;
use Illuminate\Support\Facades\DB;
use Mockery\Exception;

class Game implements GameInterface
{
    use Helpers;

    public $plat;

    public $defaultLength = 14;

    public $pre = '';

    //
    public function __construct($plat)
    {
        $platNamespace = '\\App\\Game\\' . $plat;
        if (class_exists($platNamespace)) {
            if ($this->plat === null) {
                $platClass = new \ReflectionClass($platNamespace);
                $this->plat = $platClass->newInstanceArgs();
            }
            $this->initPlatAttribute($plat);
            $this->initPlatParams();

        } else {
            throw  new Exception('Game instance error: plat namespace not exist');
        }
    }

    /**
     * gameapi
     * 2019/1/7 13:54
     * Administrator
     * initAttribute
     * 初始化平台属性
     * @param $plat
     */
    public function initPlatAttribute($plat)
    {
        foreach (config('game.' . $plat) as $key => $value) {
            if ($key == 'line') {
                $lineAttribute = $this->getLineAttribute($plat, $value);
                if (array_key_exists('lineName', $lineAttribute)) {
                    foreach ($lineAttribute as $k => $v) {
                        $this->plat->$k = $v;
                    }
                } else {
                    foreach ($lineAttribute as $k => $v) {
                        $this->plat->$k = $v;
                    }
                    $this->plat->lineName = $plat;
                }

            } elseif ($key == 'updateFiled') {
                $this->plat->$key = $value;
            } else {
                $this->plat->$key = $value;
            }

        }

    }


    public function getPlatLength()
    {
        if (property_exists($this->plat, 'pre')) {
            $preLength = !is_null($this->plat->pre) ? strlen($this->plat->pre) : strlen($this->pre);
        } else {
            $preLength = strlen($this->pre);
        }
        if (property_exists($this->plat, 'length')) {
            return !is_null($this->plat->length) ? ($this->plat->length - $preLength) : $this->defaultLength;
        } else {
            return $this->defaultLength;
        }

    }


    //创建用户
    public function createMember(Request $request)
    {
        $musername = $request->input('musername', '');
        $username = $request->input('username', '');
        $gamePlat = MainGamePlat::where('main_game_plat_code', $request->mainGamePlat)->first();
        $musername = strtoupper($musername);
        $length = $this->plat->getLength();
        $accountUserName = $this->plat->getPre() . $musername;

        $i = 0;
        if (strlen($accountUserName) > $length) {
            $accountUserName = substr($accountUserName, 0, $length);
        }
        do {
            do {
                $i++;
                if ($i > 20) {
                    return ['success' => false, 'data' => [], 'message' => '创建用户失败超过上限'];
                }
                $playGameAccount = Player::where('username', $accountUserName)->where('main_game_plats_id', $gamePlat->id)->first();
                if (strlen($accountUserName) < $length) {
                    $accountUserName = $accountUserName . str_random(1);
                } else {
                    $accountUserName = substr($accountUserName, 0, $length - 1);
                }
            } while ($playGameAccount);
            $password = str_random(8);;
            $output = $this->plat->curlCreateMember($accountUserName, $password, $request);

        } while (!$output['success']);

        if ($output['success']) {
            $site = Site::where('username', $username)->first();

            $player = new Player();
            $player->site_id = $site->id;
            $player->username = $output['data']['accountUserName'];
            $player->password = $output['data']['password'];
            $player->main_game_plats_id = $gamePlat->id;
            $player->line = $this->plat->lineName;
            if (isset($output['data']['isdemo'])) {
                $player->isdemo = $output['data']['isdemo'];
            }

            $player->save();
        }
        return $output;
    }

    public function getBalance(Request $request)
    {
        $accountUserName = $request->input('accountUserName', '');
        return $this->plat->curlGetBalance($accountUserName,$request);
    }

    public function transferIn(Request $request)
    {
        $transferid = time() . mt_rand(10000000, 99999999);
        $price = $request->input('price', '');
        $accountUserName = $request->input('accountUserName', '');
        $price = intval($price);
        $site = Site::where('username', $request->input('username'))->first();
        $gamePlat = MainGamePlat::where('main_game_plat_code', str_replace('App\\Game\\', '', $request->mainGamePlat))->first();
        $siteMainGamePlat = SiteMainGamePlat::where('site_id', $site->id)->where('main_game_plats_id', $gamePlat->id)->first();
        $player = Player::where('username', $accountUserName)->where('main_game_plats_id', $gamePlat->id)->first();

        if (is_null($siteMainGamePlat)) {
            return ['success' => false, 'data' => [], 'message' => '游戏平台未开通'];
        }

        //是否是钱转分
        $money_to_point = false;

        DB::beginTransaction();
        if ($siteMainGamePlat->score < $price) {
            //直接钱转分，比率会有提成
            $sitePlatRate = $siteMainGamePlat->rate + config('game.auto_money_to_point_rate');
            if ($price * $sitePlatRate > $site->money) {
                return ['success' => false, 'data' => [], 'message' => '分值不足'];
            } else {
                $sitescore = new SiteScore();
                $sitescore->player_id = $player->id;
                $sitescore->site_id = $site->id;
                $sitescore->main_game_plats_id = $gamePlat->id;
                $sitescore->transferid = $transferid;
                $sitescore->rate = $sitePlatRate;
                $sitescore->before_money = $site->money;
                $sitescore->recharge_money = -$price * $sitePlatRate;
                $sitescore->recharge_score = $price;
                $site->money = bcsub($site->money, bcmul($price, $sitePlatRate, 5), 5);
                $site->update();
                $sitescore->original = $siteMainGamePlat->score;
                $siteMainGamePlat->score = bcadd($siteMainGamePlat->score, $price, 5);
                $siteMainGamePlat->update();
                $sitescore->change = $siteMainGamePlat->score;
                $sitescore->now_money = $site->money;
                $sitescore->op = SiteScore::SITE_PLAT_TRANSFER_IN_AUTO;
                $sitescore->save();

                //分红计算（请不要修改顺序）
                $addScore = $price;
                $money = $price * $sitePlatRate;

                $costRate = $gamePlat->rate + config('game.cost_rate_api');
                $earning['agent_id'] = $site->agent ? $site->agent->id : 0;
                $earning['plat_id'] = $gamePlat->id;
                $earning['site_id'] = $site->id;
                $earning['rate'] = $site->agent ? $site->agent->rate : 0;
                $earning['money'] = $money;
                $earning['site_rate'] = $sitePlatRate;
                $earning['score'] = $addScore;
                $earning['plat_rate'] = $costRate;
                $earning['earnings_money'] = round(($money - $costRate * $addScore) * $earning['rate'], 6);
                $earning['sys_earnings_money'] = round($money - $costRate * $addScore, 6);
                $earning['sys_net_earnings_money'] = round(($money - $costRate * $addScore) - $earning['earnings_money'], 6);
                $earning['before_money'] = $site->agent ? $site->agent->money : 0;
                $earning['after_money'] = $site->agent ? $site->agent->money + $earning['earnings_money'] : 0;

                $money_to_point = true;
            }
        }
        $sitescore = new SiteScore();
        $sitescore->player_id = $player->id;
        $sitescore->site_id = $site->id;
        $sitescore->main_game_plats_id = $gamePlat->id;
        $sitescore->rate = isset($sitePlatRate) ? $sitePlatRate : $siteMainGamePlat->rate;
        $sitescore->before_money = $site->money;
        $sitescore->now_money = $site->money;
        $sitescore->recharge_money = 0;
        $sitescore->recharge_score = -$price;
        $sitescore->original = $siteMainGamePlat->score;
        $siteMainGamePlat->score = bcsub($siteMainGamePlat->score, $price, 5);
        $siteMainGamePlat->update();
        $sitescore->change = $siteMainGamePlat->score;
        $sitescore->transferid = $transferid;
        $sitescore->op = SiteScore::SITE_PLAT_TRANSFER_IN;
        $siteScoreResult = $sitescore->save();

        $playtransfer = new PlayerTransfer();
        $playtransfer->player_id = $player->id;
        $playtransfer->transferid = $transferid;
        $playtransfer->money = $price;
        $playtransfer->main_game_plats_id = $gamePlat->id;
        $playtransfer->direction = 1;


        //钱转分需要记录分红，分红记录保存
        if ($money_to_point && $siteScoreResult && $earning['agent_id'] != 0) {
            $earning['site_score_id'] = $sitescore->id;
            Earning::create($earning);
        }

        $output = $this->plat->curlTransferIn($accountUserName, $price, $transferid,$gamePlat->id);

        if ($output['success']) {
            if (isset($output['data']['transferid'])) {
                //转帐成功
                DB::commit();
                $playtransfer->plattransferid = $output['data']['transferid'];
                $playtransfer->status = 1;
            }
        } else {
            if ($output['data']['transfer_state'] === -1) {
                //转帐未知
                DB::commit();
                $playtransfer->status = -1;
                $sitescore->status = -1;
                $sitescore->save();
            } elseif ($output['data']['transfer_state'] === 0) {
                DB::rollBack();
                //转帐失败
                $playtransfer->status = 0;
            }
        }

        $playtransfer->save();

        return $output;
    }

    public function transferTo(Request $request)
    {
        //转出先踢线
        try {
            $this->plat->curlKick($request);
        } catch (Exception $e) {
            report($e);
        }

        $price = $request->input('price', '');
        $accountUserName = $request->input('accountUserName', '');
        $price = intval($price);
        $site = Site::where('username', $request->input('username'))->first();
        $gamePlat = MainGamePlat::where('main_game_plat_code', str_replace('App\\Game\\', '', $request->mainGamePlat))->first();
        $siteMainGamePlat = SiteMainGamePlat::where('site_id', $site->id)->where('main_game_plats_id', $gamePlat->id)->first();
        $player = Player::where('username', $accountUserName)->where('main_game_plats_id', $gamePlat->id)->first();

        if (is_null($siteMainGamePlat)) {
            return ['success' => false, 'data' => [], 'message' => '游戏平台未开通'];
        }

        $transferid = time() . mt_rand(10000000, 99999999);
        $output = $this->plat->curlTransferTo($accountUserName, $price, $transferid,$gamePlat->id);

        $playtransfer = new PlayerTransfer();
        $playtransfer->player_id = $player->id;
        $playtransfer->transferid = $transferid;
        $playtransfer->money = $price;
        $playtransfer->main_game_plats_id = $gamePlat->id;
        $playtransfer->direction = 2;

        DB::beginTransaction();
        if ($output['success']) {
            if (isset($output['data']['transferid'])) {
                //转帐成功
                $playtransfer->plattransferid = $output['data']['transferid'];
                $playtransfer->status = 1;
                $sitescore = new SiteScore();
                $sitescore->player_id = $player->id;
                $sitescore->site_id = $site->id;
                $sitescore->main_game_plats_id = $gamePlat->id;
                $sitescore->rate = $siteMainGamePlat->rate;
                $sitescore->before_money = $site->money;
                $sitescore->now_money = $site->money;
                $sitescore->recharge_score = $price;
                $sitescore->original = $siteMainGamePlat->score;
                $siteMainGamePlat->score = bcadd($siteMainGamePlat->score, $price, 5);
                $siteMainGamePlat->update();
                $sitescore->change = $siteMainGamePlat->score;

                $sitescore->transferid = $transferid;
                $sitescore->op = SiteScore::SITE_PLAT_TRANSFER_OUT;
                $sitescore->save();
                DB::commit();
            }
        } else {
            if ($output['data']['transfer_state'] === -1) {
                //转帐未知
                DB::commit();
                $playtransfer->status = -1;
            } elseif ($output['data']['transfer_state'] === 0) {
                $playtransfer->status = 0;
                DB::rollBack();
                //转帐失败
            }

        }
        $playtransfer->save();

        return $output;
    }

    public function kick(Request $request)
    {
        return $this->plat->curlKick($request);
    }

    public function lotteryCode(Request $request)
    {
        return $this->plat->curlLotteryCode($request);
    }

    public function checkTransfer(Request $request)
    {
        $response = $this->plat->curlCheckTransfer($request);
        $mainGamePlat = MainGamePlat::where('main_game_plat_code', $request->mainGamePlat)->first();
        $playerTransfer = PlayerTransfer::where('plattransferid', $request->transferid)
            ->orWhere('transferid', $request->transferid)
            ->first();

        $siteScore = SiteScore::where('transferid', $playerTransfer->plattransferid)
            ->orWhere('transferid', $playerTransfer->transferid)
            ->whereIn('op', [SiteScore::SITE_PLAT_TRANSFER_IN, SiteScore::SITE_PLAT_TRANSFER_OUT])
            ->first();
        $site_username = $request->username;
        $site = Site::where('username', $site_username)->first();
        $siteMainGamePlat = $site
            ->MainGamePlat()
            ->where('conf_site_main_game_plats.main_game_plats_id', $mainGamePlat->id)
            ->first();
        //处理未知状态
        DB::beginTransaction();
        try {
            if ($playerTransfer->status == -1) {
                if (!$response['success'] && $response['data']['transfer_state'] == 0) {
                    if ($playerTransfer->direction == PlayerTransfer::TRANSFER_TYPE_IN && !is_null($siteScore)) {
                        $score = $siteMainGamePlat->pivot->score + $playerTransfer->money;
                        $site->MainGamePlat()->updateExistingPivot($mainGamePlat->id, ['score' => $score]);
                        $playerTransfer->status = 0;
                        $siteScore->status = 0;
                        $siteScore->save();
                    } elseif ($playerTransfer->direction == PlayerTransfer::TRANSFER_TYPE_OUT) {
                        $playerTransfer->status = 0;
                    }

                } elseif ($response['success'] && $response['data']['transfer_state'] == 1) {
                    if ($playerTransfer->direction == PlayerTransfer::TRANSFER_TYPE_IN) {
                        $playerTransfer->status = 1;
                        $siteScore->status = 1;
                        $siteScore->save();
                    } elseif ($playerTransfer->direction == PlayerTransfer::TRANSFER_TYPE_OUT) {
                        $playerTransfer->status = 1;
                        $sitescore = new SiteScore();
                        $sitescore->player_id = $playerTransfer->player_id;
                        $sitescore->site_id = $site->id;
                        $sitescore->main_game_plats_id = $playerTransfer->main_game_plats_id;
                        $sitescore->rate = $siteMainGamePlat->pivot->rate;
                        $sitescore->before_money = $site->money;
                        $sitescore->now_money = $site->money;
                        $sitescore->recharge_score = $playerTransfer->money;
                        $sitescore->original = $siteMainGamePlat->pivot->score;
                        $score = bcadd($siteMainGamePlat->pivot->score, $playerTransfer->money, 5);
                        $site->MainGamePlat()->updateExistingPivot($mainGamePlat->id, ['score' => $score]);
                        $sitescore->change = $siteMainGamePlat->pivot->score;
                        $sitescore->transferid = $playerTransfer->transferid;
                        $sitescore->op = SiteScore::SITE_PLAT_TRANSFER_OUT;
                        $sitescore->status = 1;
                        $sitescore->save();
                    }
                }

                $playerTransfer->save();
                DB::commit();
            }
        } catch (Exception $exception) {
            report($exception);
            DB::rollback();
        }


        return $response;
    }

    public function joinGame(Request $request)
    {
        return $this->plat->curlJoinGame($request);
    }

    public function joinMobileGame(Request $request)
    {
        return $this->plat->curlJoinMobileGame($request);
    }

    public function getBetRecord($startTime, $endTime)
    {
        $page = 1;
        do {
            $output = $this->plat->curlGetBetRecord($startTime, $endTime, $page);
        } while ($output['success'] && ($page != $output['data']['page']));
    }


    /**
     * gameapi
     * 2018/9/24 10:55
     * Administrator
     * xmlToArray
     * xml格式转数组
     * @param $xml
     * @return mixed
     */
    public function xmlToArray($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        $val = json_decode(json_encode($xmlstring), true);
        return $val;
    }

    /**
     * gameapi
     * 2018/9/24 10:55
     * Administrator
     * request
     * 通用得请求方法
     * @param string $method
     * @param $url
     * @param array $param
     * @param null $header
     * @param null $other
     * @return string
     */
    public function request($method = 'GET', $url, $param = [], $header = null, $other = null)
    {
        $params = [
            'curl' => [
                CURLOPT_INTERFACE => config('game.pub.AddressIp')
            ]
        ];

        if (!empty($param)) {
            switch ($method) {
                case 'POST':
                    $requestParams = [
                        'form_params' => $param
                    ];
                    break;
                case 'PUT':
                    $requestParams = [
                        'form_params' => $param
                    ];
                    break;
                case 'GET':
                    $requestParams = [
                        'query' => $param
                    ];
                    break;
            }
        }

        if ($header) {
            $requestHeaders = [
                'headers' => $header
            ];
            $params = array_merge($params, $requestHeaders);
        }
        $params = isset($requestParams) ? array_merge($requestParams, $params) : $params;
        $params = !empty($other) ? array_merge($params, $other) : $params;
        $client = new Client();
        //提交各种异常报告，忽略错误
        $response = null;
        try {
            $response = $client->request($method, $url, $params);
            return $response->getBody()->getContents();
        } catch (ConnectException $e) {
            report($e);
            throw $e;
        } catch (ClientException $e) {
            report($e);
            throw $e;
        } catch (ServerException $e) {
            report($e);
            throw $e;
        } catch (TooManyRedirectsException $e) {
            report($e);
            throw $e;
        } catch (RequestException $e) {
            report($e);
            throw $e;
        } catch (Exception $e) {
            report($e);
            throw $e;
        }

    }

    //解密
    public function strToArray($datas)
    {
        $data=urldecode($datas);
        $data=base64_decode($data);
        $data=openssl_decrypt($data,'AES-128-CBC',$this->appKey,OPENSSL_RAW_DATA ,substr($this->appKey,0,16));
        $data=json_decode($data,true);
        return $data;
    }

    public function curlApiInfo($url, $data=[], $header=[],$timeOut=60)
    {
        if (!empty($data) && is_array($data))  $data=http_build_query($data);
//        \Log::info($data);
        $ch = curl_init();
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch,CURLOPT_TIMEOUT, $timeOut);
        curl_setopt($ch, CURLOPT_POST,true);
        curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
        if ($header){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        }
        try{
            $output=curl_exec($ch);
        }catch (\Throwable $e){
            return array('status'=>0,'data'=>"CURL Error String:".$e->getMessage());
        }
        $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
        //接口有可能返回false，这里严格定义
        if($output === false){
            return array('status'=>0,'data'=>"CURL Error String:".curl_error($ch));
        }
        curl_close($ch);
        $output=trim($output);
        if ($httpCode==200){
            $header=1;
        }else{
            $header=0;
        }
        return array('status'=>1,'data'=>$output,'header'=>$header);
    }

    /**
     * gameapi
     * 2019/1/7 14:45
     * Administrator
     * getLineAttribute
     * 获取线路属性
     * 请求中带有线路line参数，查找相应线路，没有则取默认
     * 若线路默认属性不存在，则取首个线路
     * @param $platName
     * @param $value
     * @param null $lineName
     * @return mixed
     */
    private function getLineAttribute($platName, $value, $lineName = null)
    {
        if (!empty(request('accountUserName'))) {
            $plat = MainGamePlat::where('main_game_plat_code', $platName)->first();
            $player = Player::where('main_game_plats_id', $plat->id)
                ->where('username', request('accountUserName'))
                ->first();
            $lineName = $player->line;
        }

        $lineAttribute = collect($value)->first(function ($item) use ($lineName) {
            if (!empty($lineName)) {
                if ($item['lineName'] == $lineName) {
                    return $item;
                }
            }

            if (!empty(request('line'))) {
                if ($item['lineName'] == request('line')) {
                    return $item;
                }
            }

            if ($item['default'])
                return $item;
        }
        );

        $lineAttribute = $lineAttribute ?? collect($value)->first();
        if (isset($player) && array_key_exists('lineName', $lineAttribute) && empty($player->line)) {
            $player->line = $lineAttribute['lineName'];
            $player->save();
        }

        if (empty($lineAttribute)) {
            throw new Exception('line params get error:get line is null', 423);
        }

        return $lineAttribute;
    }

    /**
     * gameapi
     * 2019/1/14 15:13
     * Administrator
     * initPlatParams
     * 平台参数初始化
     */
    private function initPlatParams()
    {
        if (method_exists($this->plat, 'initParams')) {
            $this->plat->initParams();
        }
    }


}